package org.spring.bean.factory.support;

import lombok.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spring.bean.factory.config.SingletonBeanRegistry;
import org.tool.core.lang.Assert;

import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 共享 bean 实例的通用注册表，实现 {@link org.springframework.beans.factory.config.SingletonBeanRegistry}。
 * 允许注册应为注册表的所有调用者共享的单例实例，以通过 bean 名称获取。
 *
 * <p>还支持注册 {@link DisposableBean} 实例（可能对应于已注册的单例，也可能不对应），以在注册表关闭时被销毁。
 * 可以注册 bean 之间的依赖关系以强制执行适当的关闭顺序。
 *
 * <p>该类主要作为{@link org.springframework.beans.factory.BeanFactory}实现的基类，分解出单例bean实例的通用管理。
 * 请注意，{@link org.springframework.beans.factory.config.ConfigurableBeanFactory} 接口扩展了 {@link SingletonBeanRegistry} 接口。
 *
 * <p>请注意，与 {@link AbstractBeanFactory} 和 {@link DefaultListableBeanFactory}（继承自它）相比，
 * 此类既不假定 bean 定义概念，也不假定 bean 实例的特定创建过程。或者也可以用作嵌套助手来委托。
 *
 * @author 李坤
 * @date 2022/4/20 15:15
 */
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {

    private final Logger logger = LoggerFactory.getLogger(DefaultListableBeanFactory.class);

    /**
     * 单例对象的缓存：bean 名称到 bean 实例。
     */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    /**
     * 依赖 bean 名称之间的映射：bean 名称到依赖 bean 名称集。
     */
    private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);

    /**
     * 依赖 bean 名称之间的映射：bean 名称到 bean 依赖项的 bean 名称集。
     */
    private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);

    @Override
    public void registerSingleton(@NonNull String beanName, Object singletonObject) {
        Assert.notNull(beanName, "beanName 不能为空");
        Assert.notNull(singletonObject, "singletonObject 不能为空");

        logger.debug("注册单例对象：{}", beanName);

        singletonObjects.put(beanName, singletonObject);
    }

    @Override
    public Object getSingleton(@NonNull String beanName) {
        return singletonObjects.get(beanName);
    }

    public void registerDependentBean(String beanName, String dependentBeanName) {
        synchronized (this.dependentBeanMap) {
            Set<String> dependentBeans = dependentBeanMap.computeIfAbsent(beanName, key -> new LinkedHashSet<>(8));

            // 如果 dependentBeans 中已存在 dependentBeanName 则中断后续操作
            if (!dependentBeans.add(dependentBeanName)) {
                return;
            }
        }

        synchronized (this.dependenciesForBeanMap) {
            Set<String> dependenciesForBean = dependenciesForBeanMap.computeIfAbsent(dependentBeanName, key -> new LinkedHashSet<>(8));
            dependenciesForBean.add(beanName);
        }
    }

    public String[] getDependentBeans(String beanName) {
        final Set<String> dependentBeans = dependentBeanMap.get(beanName);
        return dependentBeans == null ? new String[0] : dependentBeans.toArray(new String[0]);
    }

    public String[] getDependenciesForBean(String beanName) {
        final Set<String> dependentForBeans = dependenciesForBeanMap.get(beanName);
        return dependentForBeans == null ? new String[0] : dependentForBeans.toArray(new String[0]);
    }

}
